<?php
/*. require_module 'xml'; .*/
#******************************************************************************
#* Name         : PxPay_OpenSSL.inc.php
#* Description  : Classes used interact with the PxPay interface using PHP with the OpenSSL extension installed 
#* Copyright    : Direct Payment Solutions 2009(c)
#* Date         : 2009-04-10
#* Version      : 1.0
#* Author       : Thomas Treadwell
#******************************************************************************

/**
 * Use this class to parse an XML document
 */
class MifMessage
{
  /**
   * var mixed $xml_
   */
  private $xml_ = '';
  /**
   * var array[string]array $xml_index_
   */
  private $xml_index_ = array();
  /**
   * var array[int]array $xml_value_
   */
  private $xml_value_ = array();
  /**
   * Create a MifMessage with the specified XML text.
   *
   * The constructor returns a null object if there is a parsing error.
   *
   * @return void
   * @param string $xml
   */
  public function __construct($xml)
  {
    $p = xml_parser_create();
    xml_parser_set_option($p,XML_OPTION_CASE_FOLDING,0);
    $ok = xml_parse_into_struct($p, $xml, $value, $index);
    xml_parser_free($p);
    if (!empty($ok))
    {
      $this->xml_ = $xml;
      $this->xml_value_ = $value;
      $this->xml_index_ = $index;
    }
  }

  /**
   * Return the value of the specified top-level attribute.
   *
   * This method can only return attributes of the root element.
   * If the attribute is not found, return "".
   *
   * @return string
   * @param string $attribute
   */
  public function get_attribute($attribute)
  {
    /*. array[string]string .*/ $attributes = $this->xml_value_[0]["attributes"];
    return $attributes[$attribute];
  }
  /**
   * Return the index of the specified element,
   * relative to some given root element index.
   *
   * @return int
   * @param string $element
   * @param int $rootindex
   */
  private function get_element_index($element, $rootindex = 0)
  {
    #$element = strtoupper($element);
    $pos = strpos($element, "/");
    if ($pos !== false)
    {
      # element contains '/': find first part
      $start_path = substr($element,0,$pos);
      $remain_path = substr($element,$pos+1);
      $index = $this->get_element_index($start_path, $rootindex);
      if ($index == 0)
      {
        # couldn't find first part give up.
        return 0;
      }
      # recursively find rest
      return $this->get_element_index($remain_path, $index);
    }
    else
    {
      # search from the parent across all its children
      # i.e. until we get the parent's close tag.
      $level = intval($this->xml_value_[$rootindex]["level"]);
      if ($this->xml_value_[$rootindex]["type"] === "complete")
      {
        return 0;   # no children
      }
      $index = $rootindex+1;
      while ($index<count($this->xml_value_) && 
             !(intval($this->xml_value_[$index]["level"]) === $level && 
               $this->xml_value_[$index]["type"] === "close"))
      {
        # if one below parent and tag matches, bingo
        if (intval($this->xml_value_[$index]["level"]) === $level+1 &&
            $this->xml_value_[$index]["tag"] === $element)
        {
          return $index;
        }
        $index++;
      }
      return 0;
    }
  }
  /**
   * Return the text of the specified element.
   *
   * The element is given as a simplified XPath-like name.
   * For example, "Link/ServerOk" refers to the ServerOk element
   * nested in the Link element (nested in the root element).
   * If the element is not found, return "".
   *
   * @return string
   * @param string $element
   */
  function get_element_text($element)
  {
    $index = $this->get_element_index($element, 0);
    if ($index == 0)
    {
      return "";
    }
    else
    {
	#When element existent but empty
    $elementObj = $this->xml_value_[$index];
    if (! array_key_exists("value", $elementObj))
      return "";
   
    return strval($this->xml_value_[$index]["value"]);
    }
  }

}

#******************************************************************************
# Abstract base class for PxPay messages.
# These are messages with certain defined elements,  which can be serialized to XML.

#******************************************************************************
class PxPayMessage {
	/** @var string */
    public $TxnType;
	/** @var string */
	public $CurrencyInput;
	/** @var string */
  	public $TxnData1;
	/** @var string */
  	public $TxnData2;
	/** @var string */
  	public $TxnData3;
	/** @var string */
  	public $MerchantReference;
	/** @var string */
  	public $EmailAddress;
	/** @var string */
  	public $BillingId;
	/** @var string */
	public $TxnId;
	
	function __construct(){
	
	}
    /**
     * @return void
     * @param string $BillingId
     */
	function setBillingId($BillingId){
		$this->BillingId = $BillingId;
	}
    /**
     * @return string
     */
	function getBillingId(){
		return $this->BillingId;
	}
    /**
     * @return void
     * @param string $TxnType
     */
	function setTxnType($TxnType){
		$this->TxnType = $TxnType;
	}
    /**
     * @return string
     */
	function getTxnType(){
		return $this->TxnType;
	}
    /**
     * @return void
     * @param string $CurrencyInput
     */
	function setCurrencyInput($CurrencyInput){
		$this->CurrencyInput = $CurrencyInput;
	}
    /**
     * @return string
     */
	function getCurrencyInput(){
		return $this->CurrencyInput;
	}
    /**
     * @return void
     * @param string $MerchantReference
     */
	function setMerchantReference($MerchantReference){
		$this->MerchantReference = $MerchantReference;
	}
    /**
     * @return string
     */
	function getMerchantReference(){
		return $this->MerchantReference;
	}
    /**
     * @return void
     * @param string $EmailAddress
     */
	function setEmailAddress($EmailAddress){
		$this->EmailAddress = $EmailAddress;
	}
    /**
     * @return string
     */
	function getEmailAddress(){
		return $this->EmailAddress;
	}
    /**
     * @return void
     * @param string $TxnData1
     */
	function setTxnData1($TxnData1){
		$this->TxnData1 = $TxnData1;
	}
    /**
     * @return string
     */
	function getTxnData1(){
		return $this->TxnData1;
	}
    /**
     * @return void
     * @param string $TxnData2
     */
	function setTxnData2($TxnData2){
		$this->TxnData2 = $TxnData2;
	}
    /**
     * @return string
     */
	function getTxnData2(){
		return $this->TxnData2;
	}
    /**
     * @return void
     * @param string $TxnData3
     */
	function setTxnData3($TxnData3){
		$this->TxnData3 = $TxnData3;
	}
    /**
     * @return string
     */
	function getTxnData3(){
		return $this->TxnData3;
	}
    /**
     * @return void
     * @param string $TxnId
     */
	function setTxnId($TxnId)
	{
		$this->TxnId = $TxnId;
	}
    /**
     * @return string
     */
	function getTxnId(){
		return $this->TxnId;
	}
    /**
     * @return string
     */
	function toXml(){
		$arr = get_object_vars($this);

		$xml  = "<GenerateRequest>";
    	foreach ($arr as $prop => $val) {
        	$xml .= "<$prop>$val</$prop>" ;
        }

		$xml .= "</GenerateRequest>";
		return $xml;
	}
	
	
}

#******************************************************************************
# Class for PxPay request messages.
#******************************************************************************
class PxPayRequest extends PxPayMessage
{
	/** @var string */
    public $UrlFail;
    /** @var string */
    public $UrlSuccess;
    /** @var string */
	public $AmountInput;
    /** @var bool */
	public $EnableAddBillCard;
    /** @var string */
	public $PxPayUserId;
    /** @var string */
	public $PxPayKey;
    /** @var string */
	public $Opt;
	
	
 	function __construct(){
		PxPayMessage::__construct();
		
	}
	/**
     * @return void
     * @param bool $EnableBillAddCard
     */
	function setEnableAddBillCard($EnableBillAddCard){
	 $this->EnableAddBillCard = $EnableBillAddCard;
	}
	/**
     * @return void
     * @param string $UrlFail
     */
	function setUrlFail($UrlFail){
		$this->UrlFail = $UrlFail;
	}
	/**
     * @return void
     * @param string $UrlSuccess
     */
	function setUrlSuccess($UrlSuccess){
		$this->UrlSuccess = $UrlSuccess;
	}
	/**
     * @return void
     * @param float $AmountInput
     */
	function setAmountInput($AmountInput){
		$this->AmountInput = sprintf("%9.2f",$AmountInput); 
	}
	/**
     * @return void
     * @param string $UserId
     */
	function setUserId($UserId){
		$this->PxPayUserId = $UserId;
	}
	/**
     * @return void
     * @param string $Key
     */
	function setKey($Key){
		$this->PxPayKey = $Key;
	}
	/**
     * @return void
     * @param string $Opt
     */
	function setOpt($Opt){
		$this->Opt = $Opt;
	}
	

	#******************************************************************
	#Data validation 
	#******************************************************************
	function validData(){
		$msg = "";
		if($this->TxnType != "Purchase")
			if($this->TxnType != "Auth")
				$msg = "Invalid TxnType[$this->TxnType]<br>";
		
		if(strlen($this->MerchantReference) > 64)
			$msg = "Invalid MerchantReference [$this->MerchantReference]<br>";
		
		if(strlen($this->TxnId) > 16)
			$msg = "Invalid TxnId [$this->TxnId]<br>";
		if(strlen($this->TxnData1) > 255)
			$msg = "Invalid TxnData1 [$this->TxnData1]<br>";
		if(strlen($this->TxnData2) > 255)
			$msg = "Invalid TxnData2 [$this->TxnData2]<br>";
		if(strlen($this->TxnData3) > 255)
			$msg = "Invalid TxnData3 [$this->TxnData3]<br>";
			
		if(strlen($this->EmailAddress) > 255)
			$msg = "Invalid EmailAddress [$this->EmailAddress]<br>";
			
		if(strlen($this->UrlFail) > 255)
			$msg = "Invalid UrlFail [$this->UrlFail]<br>";
		if(strlen($this->UrlSuccess) > 255)
			$msg = "Invalid UrlSuccess [$this->UrlSuccess]<br>";
		if(strlen($this->BillingId) > 32)
			$msg = "Invalid BillingId [$this->BillingId]<br>";
		
		if ($msg != "") {
		    trigger_error($msg, E_USER_ERROR);
			return false;
		}
		return true;
	}

}

#******************************************************************************
# Class for PxPay response messages.
#******************************************************************************

class PxPayResponse extends PxPayMessage
{
    /** @var string $Success */
	public $Success;
    /** @var string $AuthCode */
	public $AuthCode;
    /** @var string $CardName */
	public $CardName;
    /** @var string $CardHolderName */
	public $CardHolderName;	
    /** @var string $CardNumber */
	public $CardNumber;
    /** @var string $DateExpiry */
	public $DateExpiry;
    /** @var string $ClientInfo */
	public $ClientInfo;
    /** @var string $DpsTxnRef */
	public $DpsTxnRef;
    /** @var string $DpsBillingId */
  	public $DpsBillingId;	
    /** @var string $AmountSettlement */
	public $AmountSettlement;
    /** @var string $CurrencySettlement */
	public $CurrencySettlement;
    /** @var string $TxnMac */
	public $TxnMac;
    /** @var string $ResponseText */
	public $ResponseText;
    
    /**
     * @return void
     * @param string $xml
     */
	function __construct($xml){
		$msg = new MifMessage($xml);
		PxPayMessage::__construct();
		
		$this->Success = $msg->get_element_text("Success");
		$this->setTxnType($msg->get_element_text("TxnType"));	
		$this->CurrencyInput = $msg->get_element_text("CurrencyInput");	
		$this->setMerchantReference($msg->get_element_text("MerchantReference"));	
		$this->setTxnData1($msg->get_element_text("TxnData1"));
		$this->setTxnData2($msg->get_element_text("TxnData2"));
		$this->setTxnData3($msg->get_element_text("TxnData3"));
		$this->AuthCode = $msg->get_element_text("AuthCode");
		$this->CardName = $msg->get_element_text("CardName");		
		$this->CardHolderName = $msg->get_element_text("CardHolderName");	
		$this->CardNumber = $msg->get_element_text("CardNumber");	
		$this->DateExpiry = $msg->get_element_text("DateExpiry");	
		$this->ClientInfo = $msg->get_element_text("ClientInfo");
		$this->TxnId = $msg->get_element_text("TxnId");		
		$this->setEmailAddress($msg->get_element_text("EmailAddress"));
		$this->DpsTxnRef = $msg->get_element_text("DpsTxnRef");		
		$this->BillingId = $msg->get_element_text("BillingId");
		$this->DpsBillingId = $msg->get_element_text("DpsBillingId");
		$this->AmountSettlement = $msg->get_element_text("AmountSettlement");
		$this->CurrencySettlement = $msg->get_element_text("CurrencySettlement");
		$this->TxnMac = $msg->get_element_text("TxnMac");
		$this->ResponseText = $msg->get_element_text("ResponseText");
	
	}


	function getSuccess(){
		return $this->Success;
	}
	function getAuthCode(){
		return $this->AuthCode;
	}
	function getCardName(){
		return $this->CardName;
	}
	function getCardHolderName(){
		return $this->CardHolderName;
	}
	function getCardNumber(){
		return $this->CardNumber;
	}
	function getDateExpiry(){
		return $this->DateExpiry;
	}
	function getClientInfo(){
		return $this->ClientInfo;
	}
	function getDpsTxnRef(){
		return $this->DpsTxnRef;
	}
	function getDpsBillingId(){
		return $this->DpsBillingId;
	}
	function getAmountSettlement(){
		return $this->AmountSettlement;
	}	
	function getCurrencySettlement(){
		return $this->CurrencySettlement;
	}
	function getTxnMac(){
		return $this->TxnMac;
	}
	function getResponseText(){
		return $this->ResponseText;
	}
}

class PxPay_OpenSSL
{
    /**
     * @var string
     */
	public $PxPay_Key;
    /**
     * @var string
     */
	public $PxPay_Url;
    /**
     * @var string
     */
	public $PxPay_Userid;
    /**
     * @return void
     * @param string $Url
     * @param string $UserId
     * @param string $Key
     */
    function __construct($Url, $UserId, $Key)
    {
        error_reporting(E_ERROR);
        $this->PxPay_Key = $Key;
        $this->PxPay_Url = $Url;
        $this->PxPay_Userid = $UserId;
    }
	
    #******************************************************************************
    # Actual submission of XML using OpenSSL. Returns output XML
    #******************************************************************************
    /**
     * @return string
     * @param string $inputXml
     * @param bool $isGenerateRequest
     */
    function submitXml($inputXml, $isGenerateRequest){
  
        // parsing the given URL
        $URL_Info=@parse_url($this->PxPay_Url);

        // Building referrer
        $referer=$_SERVER["SCRIPT_URI"];

        // Find out which port is needed - if not given use standard (=80)
        if(!isset($URL_Info["port"]))
            $URL_Info["port"]='443';

        // building POST-request:
        $requestdata = '';
        $requestdata.="POST ".$URL_Info["path"]." HTTP/1.1\n";
        $requestdata.="Host: ".$URL_Info["host"]."\n";
        $requestdata.="Referer: $referer\n";
        $requestdata.="Content-type: application/x-www-form-urlencoded\n";
        $requestdata.="Content-length: ".strlen($inputXml)."\n";
        $requestdata.="Connection: close\n";
        $requestdata.="\n";
        $requestdata.=$inputXml."\n";
        $fp = fsockopen("ssl://".$URL_Info["host"], intval($URL_Info["port"]));

        fputs($fp, $requestdata);
        $result = '';
        while(!feof($fp)) {
            $result .= fgets($fp, 128);
        }
        fclose($fp);
	     
        # Find where start of XML is
        if($isGenerateRequest) {
            $xmlStart = "<Request";
        } else {
            $xmlStart = "<Response";
        }
        $outputXml = strstr($result, $xmlStart);
        return $outputXml;
    }
    #******************************************************************************
    # Create a request for the PxPay interface
    #******************************************************************************
    /**
     * @return string
     * @param PxPayRequest $request
     */
    function makeRequest($request)
    {
        #Validate the Request
        if($request->validData() == false) {
            return '';
        }
        $request->setUserId($this->PxPay_Userid);
        $request->setKey($this->PxPay_Key);
        $xml = $request->toXml();
        $result = $this->submitXml($xml, true);
        return $result;		
    }
			
	#******************************************************************************
	# Return the transaction outcome details
	#******************************************************************************
	/**
     * @return PxPayResponse
     * @param string $result
     */
    function getResponse($result){
				
		$inputXml = "<ProcessResponse><PxPayUserId>".$this->PxPay_Userid."</PxPayUserId><PxPayKey>".$this->PxPay_Key.
		"</PxPayKey><Response>".$result."</Response></ProcessResponse>";
		
		$outputXml = $this->submitXml($inputXml, false);
		
		$pxresp = new PxPayResponse($outputXml);
		return $pxresp;	
	}
}

?>
